package xyz.robinbingo.liquid.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import xyz.robinbingo.liquid.constant.Constants;
import xyz.robinbingo.liquid.model.Liquid;
import xyz.robinbingo.liquid.model.TfConfig;
import xyz.robinbingo.liquid.model.XyConfig;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author Robin
 * @date 2018-08-15 12:39
 **/
@Service
@Slf4j
public class ExcelService {

    @Value("${tfConfigs}")
    private String tfConfigs;
    @Value("${tCount}")
    private int tCount;
    @Value("${fCount}")
    private int fCount;

    @Value("${xyConfigs}")
    private String xyConfigs;

    @Value("${inputPath}")
    private String inputPath;
    @Value("${inputSuffix}")
    private String inputSuffix;
    @Value("${outputPath}")
    private String outputPath;

    @Value("${expCount}")
    private int expCount;
    @Value("${fileCount}")
    private int fileCount;

    private static List<TfConfig> tfConfs = new ArrayList<>();
    private static List<XyConfig> xyConfs = new ArrayList<>();

    /****************************************public methods****************************************/

    public void handleExcel() {

        log.warn("正在处理，请稍后...");

        //----------读取配置文件----------
        readConfig();

        for (int i = 1; i <= expCount; i++) {
            // 一次实验的总记录数
            List<Liquid> liquidsAll = new ArrayList<>();
            // 每次实验第一个文件的记录数
            int l1Size = 0;
            // 每次实验是否所有文件都存在
            boolean allExist = true;

            // 判断文件是否齐全
            for (int m = 1; m <= fileCount; m++) {
                int n = i * 10 + m;
                String inputFilePath = inputPath + n + inputSuffix;
                if (!FileUtil.exist(inputFilePath)) {
                    allExist = false;
                    break;
                }
            }

            if (allExist) {
                //----------读取输入excel----------
                for (int j = 1; j <= fileCount; j++) {
                    int k = i * 10 + j;
                    String inputFilePath = inputPath + k + inputSuffix;

                    List<Liquid> liquids = ExcelUtil.getReader(inputFilePath).readAll(Liquid.class);

                    if (j == 1) {
                        // 第一个文件
                        l1Size = liquids.size();
                    } else {
                        // 第二个文件
                        int finalL1Size = l1Size;
                        liquids.forEach(l2 -> l2.set序号(finalL1Size + l2.get序号()));
                    }

                    liquidsAll.addAll(liquids);
                }

                String realOutputPath = outputPath + "\\" + i + "\\";

                //----------输出稳定点信息----------
                tfResult(l1Size, liquidsAll, realOutputPath);

                //----------输出坐标点信息----------
                xyResult(l1Size, liquidsAll, realOutputPath);

                //----------输出区间内全部信息----------
                rangeResult(l1Size, liquidsAll, realOutputPath);

                log.warn("处理完成，快到目标文件夹里寻找答案！:)");
            }
        }
    }


    /****************************************private methods****************************************/

    /**
     * 读取配置文件
     *
     * @author Robin
     * @date 2018/10/30 15:22
     **/
    private void readConfig() {

        JSONArray tfArray = JSONUtil.parseArray(tfConfigs);
        tfArray.forEach(o -> {
            String conf = (String) o;
            String[] split = conf.split("-");
            TfConfig tfConfig = new TfConfig();
            tfConfig.setFieldName(split[0]);
            tfConfig.setLow(Integer.parseInt(split[1]));
            tfConfig.setHigh(Integer.parseInt(split[2]));
            tfConfs.add(tfConfig);
        });

        JSONArray xyArray = JSONUtil.parseArray(xyConfigs);
        xyArray.forEach(o -> {
            String conf = (String) o;
            String[] split = conf.split("-");
            XyConfig xyConfig = new XyConfig();
            xyConfig.setX(split[0]);
            xyConfig.setStart(Integer.parseInt(split[1]));
            xyConfig.setStep(Integer.parseInt(split[2]));
            xyConfig.setTotal(Integer.parseInt(split[3]));
            xyConfig.setEnd(Integer.parseInt(split[4]));
            xyConfs.add(xyConfig);
        });
    }

    /**
     * 输出稳定点信息
     *
     * @param l1Size
     * @param liquidsAll
     * @param realOutputPath
     * @author Robin
     * @date 2018/10/30 15:22
     **/
    private void tfResult(int l1Size, List<Liquid> liquidsAll, String realOutputPath) {

        String desPath = realOutputPath + "1.稳定点信息.xls";
        ExcelWriter writer = ExcelUtil.getWriter(desPath);
        writer.writeHeadRow(Arrays.asList(Constants.TF_HEADER));

        tfConfs.forEach(c -> {
            String fieldName = c.getFieldName();
            Liquid stable = getStable(fieldName, c.getLow(), c.getHigh(), liquidsAll);
            String[] s = new String[4];
            if (stable != null) {
                s[0] = fieldName;
                Integer 序号 = stable.get序号();
                s[1] = 序号 < l1Size ? "1" : "2";
                s[2] = String.valueOf(序号 < l1Size ? 序号 : 序号 - l1Size);
                s[3] = String.valueOf(ReflectUtil.getFieldValue(stable, "总流量"));
            } else {
                s[0] = fieldName;
                s[1] = "不存在！";
                s[2] = s[3] = "";
            }
            writer.writeRow(Arrays.asList(s));
        });

        writer.flush();
    }

    /**
     * 输出坐标点信息
     *
     * @param l1Size
     * @param liquidsAll
     * @param realOutputPath
     * @author Robin
     * @date 2018/10/30 15:23
     **/
    private void xyResult(int l1Size, List<Liquid> liquidsAll, String realOutputPath) {

        xyConfs.forEach(c -> {
            String x = c.getX();
            int start = c.getStart();
            int step = c.getStep();
            int total = c.getTotal();
            int end = c.getEnd();
            int realEnd = end + start;

            String desPath = realOutputPath + "2.整数点-" + x + ".xls";
            ExcelWriter writer = ExcelUtil.getWriter(desPath);
            writer.writeHeadRow(Arrays.asList(Constants.XY_HEADER));

            // 当前x轴点的值
            IntStream.rangeClosed(1, total)
                    .map(i -> start + step * (i - 1))
                    .mapToObj(point -> getXY(l1Size, liquidsAll, point, x, start))
                    .map(Arrays::asList)
                    .forEach(writer::writeRow);

            writer.writeRow(Arrays.asList(getXY(l1Size, liquidsAll, realEnd, x, start)));

            writer.flush();
        });
    }

    /**
     * 选取区间的所有信息
     *
     * @param l1Size
     * @param liquidsAll
     * @param realOutputPath
     * @author Robin
     * @date 2018/10/30 16:13
     **/
    private void rangeResult(int l1Size, List<Liquid> liquidsAll, String realOutputPath) {
        xyConfs.forEach(c -> {
            int start = c.getStart();
            int end = c.getEnd();
            String x = c.getX();
            int realEnd = end + start;

            String desPath = realOutputPath + "3.所有点-" + x + ".xls";
            ExcelWriter writer = ExcelUtil.getWriter(desPath);
            writer.writeHeadRow(Arrays.asList(Constants.XY_HEADER));

            Liquid min = getNearest(liquidsAll, start, x);
            Liquid max = getNearest(liquidsAll, realEnd, x);

            liquidsAll.stream()
                    .filter(liquid -> {
                        Integer 序号 = liquid.get序号();
                        return 序号 >= min.get序号() && 序号 <= max.get序号();
                    })
                    .forEach(l -> {
                        BigDecimal[] bd = new BigDecimal[4];
                        bd[0] = NumberUtil.round((Double) ReflectUtil.getFieldValue(l, x) - start, 2);
                        bd[1] = BigDecimal.valueOf((Double) ReflectUtil.getFieldValue(l, "总流量"));
                        Integer 序号 = l.get序号();
                        bd[2] = BigDecimal.valueOf(序号 < l1Size ? 1 : 2);
                        bd[3] = BigDecimal.valueOf(序号 < l1Size ? 序号 : 序号 - l1Size);
                        writer.writeRow(Arrays.asList(bd));
                    });
            writer.flush();
        });
    }

    /**
     * 获取稳定点
     *
     * @param fieldName
     * @param low
     * @param high
     * @param list
     * @return Liquid
     * @author Robin
     * @date 2018/10/30 15:24
     **/
    private Liquid getStable(String fieldName, int low, int high, List<Liquid> list) {

        List<Liquid> filtered = list.stream()
                .filter(liquid -> {
                    Double 总流量 = liquid.get总流量();
                    return 总流量 >= low && 总流量 <= high;
                })
                .collect(Collectors.toList());


        for (int i = 0; i <= filtered.size() - tCount; i++) {
            boolean valid = IntStream.range(i, i + tCount)
                    .noneMatch(j -> "F".equals(ReflectUtil.getFieldValue(filtered.get(j), fieldName)));

            if (valid) {
                int count = 0;
                Liquid liquidI = filtered.get(i);
                Integer 序号 = liquidI.get序号();
                for (int k = 序号 - 1; k < list.size(); k++) {
                    if ("F".equals(ReflectUtil.getFieldValue(list.get(k), fieldName))) {
                        count++;
                        if (count > fCount) {
                            break;
                        }
                    }
                }
                if (count <= fCount) {
                    return liquidI;
                }
            }
        }

        return null;
    }

    /**
     * 获取最近点
     *
     * @param liquidsAll
     * @param pointX
     * @param fieldName
     * @return Liquid
     * @author Robin
     * @date 2018/10/30 15:26
     **/
    private Liquid getNearest(List<Liquid> liquidsAll, int pointX, String fieldName) {
        return liquidsAll.stream()
                .min(Comparator.comparingDouble(o -> Math.abs((Double) ReflectUtil.getFieldValue(o, fieldName) - pointX)))
                .orElse(null);
    }

    /**
     * 获取坐标点信息
     *
     * @param l1Size
     * @param liquidsAll
     * @param point
     * @param x
     * @param start
     * @author Robin
     * @date 2018/10/30 15:41
     **/
    private BigDecimal[] getXY(int l1Size, List<Liquid> liquidsAll, int point, String x, int start) {
        Liquid nearest = getNearest(liquidsAll, point, x);
        BigDecimal[] bd = new BigDecimal[4];
        bd[0] = NumberUtil.round((Double) ReflectUtil.getFieldValue(nearest, x) - start, 2);
        bd[1] = BigDecimal.valueOf((Double) ReflectUtil.getFieldValue(nearest, "总流量"));
        Integer 序号 = nearest.get序号();
        bd[2] = BigDecimal.valueOf(序号 < l1Size ? 1 : 2);
        bd[3] = BigDecimal.valueOf(序号 < l1Size ? 序号 : 序号 - l1Size);
        return bd;
    }
}
